# -*- coding: utf-8 -*-
from zope import component

from zope.pagetemplate.interfaces import IPageTemplate
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.form.interfaces import WidgetInputError
from zope.app.form.interfaces import MissingInputError
from zope.interface import providedBy, alsoProvides
from zope.interface.common import idatetime
import pytz
import datetime
from zope.traversing.api import getPath
from zope.exceptions.interfaces import UserError

from zope.formlib import form

from utils import ENTRY_FIELD_PREFIX
from adapters import DataAdapter
from fields import EntryField

from zope.i18nmessageid import MessageFactory
_ = MessageFactory("dbformlib")


class DBFormlibForm(form.Form):
    label = u'Crud body form'
    crud_id = ''
    actions = []
    template = ViewPageTemplateFile('pageform.pt')
    
    def __call__(self, *args, **kw):
        self.define_fields()
        self._distribute_fields()
        self.form_fields.omit('__parent__', '__name__')
        return super(DBFormlibForm, self).__call__(*args, **kw)
    
    def define_fields(self):
        """ Should be overriden by descendant classes
            Function that should define fields and widgets for fields like:
            form_fields = form.Fields(IDBFormlibContent)
            form_fields["now"].custom_widget = getCustomWidget
            form_fields["tnow"].custom_widget = DatetimeWidget
            Here we also define entry fields with
            entry_fields method
        """
        raise UserError('Not implemented!')
        
    def _distribute_fields(self):
        """ Distinguishes fields between 'entry' fields
            and 'form' fields. It is necessary because of 'in.'
            prefix for entry fields
        """        
        names = []
        for form_field in self.form_fields:
            if form_field.field.__class__ is EntryField:
                names.append(form_field.__name__)
        
        self._entry_fields = self.form_fields.select(*names)
        self.form_fields = self.form_fields.omit(*names)
        
    def validate(self, action, data):
        """ We have to take care about entry widgets too
        """
        if self.entry_widgets:
            return (form.getWidgetsData(self.widgets, self.prefix, data)
                + form.getWidgetsData(self.entry_widgets, \
                                      ENTRY_FIELD_PREFIX, data)
                + form.checkInvariants(self.form_fields, data))
        return (form.getWidgetsData(self.widgets, self.prefix, data)
                + form.checkInvariants(self.form_fields, data))
    
    def setUpWidgets(self, ignore_request=False, for_display=False):
        data = {}

        da = self.get_data_obj()
        data = da.get_widgets_data(self.actions, ignore_request=False)
        
        self.adapters = {}        
        fields = []
        self.entry_widgets = []
        # entry fields
        if self._entry_fields:
            self.entry_widgets = form.setUpDataWidgets(\
                                        form_fields=self._entry_fields,
                                        form_prefix=ENTRY_FIELD_PREFIX, 
                                        context=self.context, 
                                        request=self.request, 
                                        data=data,
                                        for_display=False,
                                        ignore_request=False)
        else:
            fields = self.form_fields
        self.widgets = form.setUpDataWidgets(form_fields=self.form_fields,
                                        form_prefix=self.prefix, 
                                        context=self.context, 
                                        request=self.request, 
                                        data=data,
                                        for_display=for_display,
                                        ignore_request=ignore_request)
    
    def setWidgetError(self, name, v):
        """ Helper function for validation purposes.
            Sets error for widget
             @name - field name
             @v - error message
        """
        w = self.widgets.get(name)
        if w:
            w._error = WidgetInputError(
              self.context.__name__, w.label, v)
        return w._error

    def get_data_obj(self):
        """ Returns adapted content object. Adapter provides 
            functions to get and set data.
        """
        return DataAdapter(self)
                           
    #def update(self):
    #    form.FormBase.update(self)
        
    def update(self):
        self.setUpWidgets()
        self.form_reset = False

        data = {}
        errors, action = form.handleSubmit(self.actions, data, self.validate)
        self.errors = errors

        if errors:
            self.status = _('There were errors')
            result = action.failure(data, errors)
        elif errors is not None:
            result = action.success(data)
            if not self.errors:
                self.form_reset = True
            else:
                self.status = _('There were errors')
                result = action.failure(data, errors)
        else:
            result = None
        

class DBFormlibEditForm(DBFormlibForm):
    crud_id = 'edit'
    label = u'Edit data'
    
    @form.action(_("Apply"), condition=form.haveInputWidgets)
    def handle_edit_action(self, action, data):
        da = self.get_data_obj()
        changed = False
        # apply_changes checks if there were any changes
        changed, result, errors = da.apply_data(self.form_fields, data)                
        if errors:
            self.errors = errors            
        elif changed:
            formatter = self.request.locale.dates.getFormatter(
                'dateTime', 'medium')

            try:
                time_zone = idatetime.ITZInfo(self.request)
            except TypeError:
                time_zone = pytz.UTC
                
            status = _("Updated on ${date_time}",
                        mapping={'date_time':
                                formatter.format(
                                    datetime.datetime.now(time_zone)
                                    )
                                }
                       )
            self.status = status
        else:
            self.status = _('No changes')
            
            
class DBFormlibAddForm(DBFormlibForm):
    crud_id = 'add'
    label = u'Add data'
    
    @form.action(_("Add"), condition=form.haveInputWidgets)
    def handle_add_action(self, action, data):
        da = self.get_data_obj()
        result, errors = da.set_data(self.form_fields, data)
        self.result = result
        if errors:
            self.errors = errors            
        else:
            self._finished_add = True
            
    _finished_add = False

    def render(self):
        if self._finished_add:
            self.request.response.redirect(self.nextURL(self.result))
            return ""
        return super(DBFormlibAddForm, self).render()

    def nextURL(self, result):
        return self.context.nextURL(self.crud_id)


class DBFormlibDeleteForm(DBFormlibForm):
    crud_id = 'delete'
    label = u'Delete data'
    
    def setUpWidgets(self, ignore_request=False, for_display=False):
        super(DBFormlibDeleteForm, self).setUpWidgets(
                                    ignore_request=ignore_request, 
                                    for_display=True)
    
    @form.action(_("Delete"))
    def handle_delete_action(self, action, data):
        da = self.get_data_obj()
        result, errors = da.set_data(self.form_fields, data)
        if errors:
            self.errors = errors            
        else:
            self._finished_del = True
            
    _finished_del = False

    def render(self):
        if self._finished_del:
            self.request.response.redirect(self.nextURL())
            return ""
        return super(DBFormlibDeleteForm, self).render()

    def nextURL(self):
        return self.context.nextURL(self.crud_id)
        
        
class DBFormlibViewForm(DBFormlibForm):
    crud_id = 'view'
    label = u'View data'
    
    def setUpWidgets(self, ignore_request=False, for_display=False):
        super(DBFormlibViewForm, self).setUpWidgets(
                                    ignore_request=ignore_request, 
                                    for_display=True)
    
    @form.action(_("OK"))
    def handle_ok_action(self, action, data):
        self.request.response.redirect(self.nextURL())
        return ''

    def nextURL(self):
        return self.context.nextURL(self.crud_id)